<h1>OpenSocial Tic-Tac-Toe</h1>

<script type="text/javascript">

// for storage chapter - use cookies or app data store
var useCookieStorage = false;

//app data store code version - V1 (direct) or V2 (local store)
var getAppDataV1 = false;

/**
 * Tick Tack Toe static object controller namespace
 */
var TTT = {
	Keys: {
		NAME: "name",
		SCORE: "score"
	},
	RequestKeys: {
		VIEWERDATA: "viewer_data",
		VIEWER: "v"
	},
	Players: {
		Computer: "O",
		Human: "X"
	},
	/**
	 * Actual Tick Tack Toe game engine.
	 */
	Game: function(){
		
		var me = this;
		var cellbackEmpty = "#555";
		var cellbackWin = "#88E";
		
		
		/**
		 * Clear the game and reset
		 */
		this.clear = function(){
			var i;
			for(i = 0; i < this.gameCells.length; i++){
				this.gameCells[i].innerHTML = "";
				this.gameCells[i].style.backgroundColor = cellbackEmpty;
			}
			this.moves = new Array(9);
			this.hasWinner = false;
			this.winningCells = null;
		}
		
		/**
		 * Colors background on winning cells
		 */
		this.markWinningCells = function(){
			if(!this.winningCells) return;
			var cell;
			for each(cell in this.winningCells){
				this.gameCells[cell].style.backgroundColor = cellbackWin;			
			}
		}
		
		/**
		 * Flag to indicate a winner was found
		 */
		this.hasWinner = false;
		
		/**
		 * Identify the cells in the winning line
		 */
		this.winningCells = null;
		
		/**
		 * Last person to win - player or computer
		 */
		this.lastWinner = null;
		
		function init(){
			me.displayBoardElem = document.getElementById("gameboard");
			me.gameCells = me.displayBoardElem.getElementsByTagName("td");
			me.moves = new Array(9);
			me.messageBlock = document.getElementById("messages");
			me.clear();
		}

		init();
	}
}

/**
 * Game object method defintions
 */
TTT.Game.prototype = {
	
	writeMessage: function(message, color){
		if(color){
			this.messageBlock.innerHTML += "<span style='color:" + color + ";'>" + message + "</span>";
		}
		else{
			this.messageBlock.innerHTML += message;
		}
	},
	
	/**
	 * Makes a move and records the result.
	 * Draws the move on the game board
	 * @param {Object} player
	 * @param {Object} position
	 */
	makeMove : function(player, position){
		this.lookForWin();
		if(!this.hasWinner){
			if(!this.moves[position]){
				this.moves[position] = player;
				this._drawMove(player, position);
				this.lookForWin();
			}
			else{
				writeMessage("Invalid Move for " + player, "red");
			}
		}
		if(this.hasWinner){
			this.markWinningCells();
		}
	},
	/**
	 * Draws the player's mark in the cell
	 * @param {Object} player
	 * @param {Object} position
	 */
	_drawMove: function(player, position){
		this.gameCells[position].innerHTML = player;
	},
	
	/**
	 * Intense AI to compete in most difficult tick-tack-toe tournaments
	 */
	makeComputerMove : function(){
		var die = 50;
		var pos;
		var found = false;
		var i=0;
		while(!found && i++ < die){
			pos = new Date().getMilliseconds() % 10;
			if(pos == 9) pos = 8;
			found = (!this.moves[pos]);
		}
		if(!found){
			for(i=0; i < 9; i++){
				if(!this.moves[i]){
					this.makeMove(TTT.Players.Computer, i);
					return;
				}
			}
			alert('no move');
			return;
		}
		else{
			this.makeMove(TTT.Players.Computer, pos);
		}
	},
	
	
	/**
	 * Examines the game board to determine if one of the players
	 * has won the game.
	 */
	lookForWin: function(){
		//horizontal
		var i=0;
		for(i=0; i<9; i=i+3){
			this.hasWinner = this.matchMovePositions(i,i+1,i+2);
			if(this.hasWinner){
				this.winningCells = [i,i+1,i+2]
				return;
			}
		}
		//vertical
		for(i=0; i<3; i++){
			this.hasWinner = this.matchMovePositions(i,i+3,i+6);
			if(this.hasWinner){
				this.winningCells = [i,i+3,i+6]
				return;
			}
		}
		//diag
		
		if(!this.hasWinner){
			this.hasWinner = this.matchMovePositions(0,4,8);
			if(this.hasWinner){
				this.winningCells = [0,4,8]
			}
		}
		if(!this.hasWinner){
			this.hasWinner = this.matchMovePositions(2,4,6);
			if(this.hasWinner){
				this.winningCells = [2,4,6]
			}
		}
	
	},
	
	/**
	 * Tests three positions to see if they match
	 * @param {Object} a
	 * @param {Object} b
	 * @param {Object} c
	 */
	matchMovePositions : function(a, b, c){
		if(this.moves[a]){
			return (this.moves[a] == this.moves[b] && this.moves[a] == this.moves[c]);
		}
		return false;
	}
}


/**
 * Set a new persistent cookie (or overwrite existing)
 * @param {Object} key
 * @param {Object} value
 */
function setCookie(key, value){
	var cook = key + '=' + value + '; expires=1 Jan 2050; path=/';
	document.cookie=cook;
}

/**
 * Read the given cookie
 * @param {Object} key
 */
function readCookie(key){
	var cookies = document.cookie;
	var keyName = key + '=';
	var pos = cookies.indexOf(keyName);
	if(pos > -1){
		pos += keyName.length;
		var endPos = cookies.indexOf(';', pos);
		if(-1 == endPos){
			endPos = cookies.length;
		}
		return cookies.substring(pos, endPos);
	}
	return null;
}



var viewer = null;


var currentSetCallback = null;


/**
 * Sets the persistent value for the game in the 
 * chosen data store
 * @param {String} key
 * @param {String} value
 */
function setGameData(key, value){
	if(useCookieStorage){
		setCookie(key, value);
		getInitialData()
	}
	else{
		setAppDataKey(key, value);
	}
}


function setAppDataKey(key, value) {
  currentSetCallback = getInitialData;

  var req = opensocial.newDataRequest();
  req.add(req.newUpdatePersonAppDataRequest(opensocial.DataRequest.PersonId.VIEWER, key, value), "set_" + key);
  req.send(setDataCallback);
};


var myLocalAppData = {}; //empty object hash for local appdata
var appdataKey = "mydata"; //key to use in dataRequest for retrieval

/**
 * Loads some or all of the AppData into a local store variable myLocalAppData
 * @param {Array} fields  Array of fields to retrieve, or null for all fields
 * @param {Function} loadedCallback  Callback handler to fire when App Data has loaded.
 */
function loadAppData(fields, loadedCallback){
	var req = opensocial.newDataRequest();
	if(fields == null){
		fields = "*"; //default to all data
	}
	req.add(req.newFetchPersonAppDataRequest(opensocial.DataRequest.PersonId.VIEWER, fields), appdataKey);
	var data; //this will have DataResponse placed in it
	req.send(function(data){
		loadAppDataCallback(data, loadedCallback)
	});
}

/**
 * Callback wrapper method for loadAppData.  
 * This loads all the data into myLocalAppData
 * and triggers the loadedCallback function, if specified
 * @param {Object} data
 * @param {Function} loadedCallback
 */
function loadAppDataCallback(data, loadedCallback){
	logDataError(data);
	if (data.hadError()) {
		return; //exit if nothing found
	}
	var mydata = data.get(appdataKey).getData();
	//app data has an additional layer of indirection
	//we reassign mydata object to the data map object
	//circumvent viewer lookup by getting key
	var ok = false;
	for(var vkey in mydata){
		mydata = mydata[vkey];
		ok = true;
		break; 
	}
	if(ok){
		var key
		for(key in mydata){
			myLocalAppData[key] = mydata[key];
		}
	}
	
	if(loadedCallback && typeof(loadedCallback) == "function"){
		loadedCallback(myLocalAppData);
	}
}


function setDataCallback(data){
	logDataError(data);
	if(currentSetCallback != null){
		currentSetCallback(data);
		currentSetCallback = null;
	}
}

function setNameData(){
	var elem = document.getElementById("txtName");
	if(elem){
		setGameData(TTT.Keys.NAME, elem.value);
	}
}

/**
 * Data initializer to load stored information
 */
function getInitialData(){
	
	if(useCookieStorage){
		if(viewer == null){
			getViewerData();
		}
		writeNameAndScore(readCookie(TTT.Keys.NAME), readCookie(TTT.Keys.SCORE));
	}
	else{
		if(getAppDataV1){
			getInitialAppDataV1();
		}
		else{
			getInitialDataV2();
		}		
	}
}

/**
 * Get the discrete app data and initialize the view
 */
function getInitialAppDataV1(){
	var req = opensocial.newDataRequest();
	var fields = [ TTT.Keys.NAME, TTT.Keys.SCORE ];
	req.add(req.newFetchPersonAppDataRequest(opensocial.DataRequest.PersonId.VIEWER, fields), TTT.RequestKeys.VIEWERDATA);
	if(viewer == null){
		req.add(req.newFetchPersonRequest(opensocial.DataRequest.PersonId.VIEWER), TTT.RequestKeys.VIEWER);
	}
	
	req.send(getDataCallbackV1);
}


/**
 * Refactored load of initial data.  This utilizes the local store
 */
function getInitialDataV2(){
	if(viewer == null){
		getViewerData();
	}
	loadAppData(null, getDataCallbackV2);
}

/**
 * Callback handler for getting AppData and viewer
 * @param {Object} data
 */
function getDataCallbackV1(data){
	logDataError(data);
	var mydata = data.get(TTT.RequestKeys.VIEWERDATA).getData();
	if(!viewer){
		viewer = data.get(TTT.RequestKeys.VIEWER).getData();
	}
	//this happens if app not installed
	if(!viewer){
		log("<b>APP NOT INSTALLED!</b>")
		return;
	}
	else{
		mydata = mydata[viewer.getId()];
		writeNameAndScore(mydata[TTT.Keys.NAME], mydata[TTT.Keys.SCORE]);
	}
}

/**
 * Loads the data from a hash object passed in
 * @param {Object} localAppData
 */
function getDataCallbackV2(localAppData){
	if (viewer == null) {
		getViewerData();
	}
	
	var nameJson = gadgets.json.parse(localAppData[TTT.Keys.NAME]);
	
	writeNameAndScore(localAppData[TTT.Keys.NAME], localAppData[TTT.Keys.SCORE]);
}



/**
 * Get just the viewer data from a personRequest
 */
function getViewerData(){
	var req = opensocial.newDataRequest();
	req.add(req.newFetchPersonRequest(opensocial.DataRequest.PersonId.VIEWER), TTT.RequestKeys.VIEWER);
	req.send(getViewerCallback);
}


/**
 * Callback handler to initialize the viewer object from a datarequest
 * @param {Object} data
 */
function getViewerCallback(data){
	logDataError(data);
	viewer = data.get(TTT.RequestKeys.VIEWER).getData();

	if(!viewer){
		log("<b>APP NOT INSTALLED!</b>")
		return;
	}
}




/**
 * Logs a data request error to the messages div
 * @param {Object} data
 */
function logDataError(data){
	if (data.hadError()) {
		//hack to discover keys
		if(data.responseItems_){
			var key;
			var curData;
			for (key in data.responseItems_){
				curData = data.get(key);
				if(curData.hadError()){
					log(curData.getErrorMessage());
				}
			}
		}
	}
}

function log(message){
	var errFeed = document.getElementById("messages");
	if(errFeed){
		errFeed.innerHTML += message + "<br />";
	}
}

/**
 * Write the name value and score value to the myinfo div
 * @param {Object} name
 * @param {Object} score
 */
function writeNameAndScore(name, score){
	var elem = document.getElementById("myinfo");
	if(elem){
		var str = "<h1>";
		if(!name){
			if(viewer){
				str += viewer.getDisplayName();
			}
		}
		else{
			str += name;
		}
		str += "<br />\nScore: <span style='color:red;'>";
		if(!score){
			str += "[no games]";
		}
		else{
			str += score;
		}
		str += "</span></h1>";
		elem.innerHTML =  str;
	}	
}

var currentGame = null;

function makePlayerMove(e){
	if(!e) var e = window.event;

	alert(e.id);
	var cell = e.target.getAttribute("gamecell");
	currentGame.makeMove(TTT.Players.Human, cell);
	if(!currentGame.hasWinner){
		window.setTimeout(function(){
			currentGame.makeComputerMove();
		}, 500);
	}
}


function initializeGame(){
	getInitialData();
	if(currentGame){
		currentGame.clear();
	}
	else{
		currentGame = new TTT.Game();
	}
}


function goToSurface(surfaceName){
	var surfaces = gadgets.views.getSupportedViews();
	var surfaceRef = surfaces[surfaceName];
	if(surfaceRef){
		opensocial.requestNavigateTo(surfaceRef);
	}

}


function getOtherNameData(){
	debugger;
	var fld = document.getElementById("txtOtherUserID");
	var id = fld.value;

	var req = opensocial.newDataRequest();
	var fields = [ TTT.Keys.NAME, TTT.Keys.SCORE ];
	req.add(req.newFetchPersonAppDataRequest(opensocial.DataRequest.PersonId.VIEWER, fields), TTT.RequestKeys.VIEWERDATA);
	if(viewer == null){
		req.add(req.newFetchPersonRequest(id), "myotherkey");
	}
	
	req.send(getOtherNameDataCallback);
	
}

function getOtherNameDataCallback(data){
	if (data.hadError()) {
		return; //exit if nothing found
	}
	var x = data.get("myotherkey");
	var mydata = x.getData();
	//app data has an additional layer of indirection
	//we reassign mydata object to the data map object
	//circumvent viewer lookup by getting key
	var ok = false;
	for(var vkey in mydata){
		mydata = mydata[vkey];
		ok = true;
		break; 
	}
	if(ok){
		var key
		for(key in mydata){
			myLocalAppData[key] = mydata[key];
		}
	}
}


/**
 * Obtains the selected radio from radio input control(s)
 * @param {Object} radioInput
 */
function getRadioValue(radioInput){
	  var checkedId=0, i;
	  if(radioInput){
	  	for(i=0; i < radioInput.length;i++){
			if(radioInput[i].checked){
				checkedId=i;
				break;
			}
		}
	  	return radioInput[checkedId].value;
	  }
	  return null;
}

/**
 * Obtains the selected option from a single item select list
 * @param {Object} selectInput
 */
function getSelectValue(selectInput){
	  if(!selectInput) return null;
	  if(selectInput.selectedIndex == -1){
	  	return null;
	  }
	  return selectInput[selectInput.selectedIndex].value;
}


/**
 * Loads a digg feed and displays it
 * @param {Object} useJsonP
 */
function loadFeed(){
    var feedUrl = "http://www.digg.com/rss/index.xml"; //default to digg
	var errElem = document.getElementById("feedItemErrors");
	errElem.innerHTML = "";

	var elem = document.getElementById("rssFeed");
	elem.innerHTML = "...Loading feed...";
      
	  var picked = document.forms[0].elements['feedSource'];
	  feedUrl = getSelectValue(picked);
	  
	  var typeRadio = document.forms[0].elements['feedformat'];
	  var formatVal = getRadioValue(typeRadio);
	  
	  var refreshFeed = document.getElementById("chkRefreshFeed").checked;
	  var interval = 0;
	  if(refreshFeed){
	  	interval = 3 * 60 * 1000; // every three minutes
	  }
	  
	  var format;
	  var subformat = null;
	  switch(formatVal){
	  	case "FEED":
			format = gadgets.io.ContentType.FEED;
			break;
	  	case "DOM":
			format = gadgets.io.ContentType.DOM;
			break;
	  	case "DOMXSL":
			format = gadgets.io.ContentType.DOM;
			subformat = "XSL";
			break;
	  	default: //text
			format = gadgets.io.ContentType.TEXT;
			break;
	  }
	  
	  loadFeedWithOptions(feedUrl, format, interval);
}

/**
 * MakeRequest call with resolved parameters.
 * @param {Object} url
 * @param {Object} format
 */
function loadFeedWithOptions(url, format, refreshInterval){
	// Initialize the request to treat it as a feed
	var params = {};
	params[gadgets.io.RequestParameters.CONTENT_TYPE] = format;
	params[gadgets.io.RequestParameters.NUM_ENTRIES] = 5;	  
	//	  params[gadgets.io.RequestParameters.GET_SUMMARIES] = true;
	//params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.TEXT;
	
	var callback = function(response){
			feedCallback(response, format);
	}
	gadgets.io.makeRequest(url, callback, params);
	
	if(refreshInterval && refreshInterval > 0){
		window.setTimeout(function(){loadFeedWithOptions(url, format, refreshInterval);}, refreshInterval);
	}
}

function feedCallback(response, format, subformat){
	var errElem = document.getElementById("feedItemErrors");
	errElem.innerHTML = "";
	if(response.errors && response.errors.length > 0){
		errElem.innerHTML = "Error: " + response.errors[0];
		showFeedResults(response.text, gadgets.io.ContentType.TEXT);
	}
	else{
		showFeedResults(response.data, format, subformat);
	}
	
}

/**
 * Display feed results.  Parsing depends on format specified.
 * @param {Object} feed
 * @param {Object} format
 */
function showFeedResults(feed, format, subformat){
	var elem = document.getElementById("rssFeed");
	elem.innerHTML = "";
	var titleElem = document.createElement("h3");
	titleElem.style.marginTop="0";
	titleElem.style.paddingTop="0";
	
	var title, copyright, items, desc;
	var i, entries;
	var line, link, body;
	var item;
	copyright=null;

	var tmpElem, tmp;
	var feedBody = document.createElement("div");

	if(format==gadgets.io.ContentType.FEED){
		title = feed.title;
		copyright = feed.copyright;
		items = feed.items;
		entries = Math.min(items.length, 5);

		//iterate feed items
		for(i=0; i < entries; i++){
			item = items[i];
			line = document.createElement("div");
			body = document.createElement("div");
			link = document.createElement("a");
			line.appendChild(link);
			link.href = item.link;
			link.target = "_blank";
			link.innerHTML = item.title;
			if(item.summary){
				body.innerHTML = item.summary;
			}
			else{
				body.innerHTML = item.description.substr(0, Math.min(120, item.description.length));
			}
			line.appendChild(body);
			
			feedBody.appendChild(line);		
		}
		
	}
	else if (format == gadgets.io.ContentType.DOM && subformat == "XSL") {
		displayXmlData(feed, createXmlDocFromString(feedTransform),elem);
		return; //just exit after transform here
	}
	else if(format==gadgets.io.ContentType.DOM){
		/*
		 * Structure of an RSS XML document:
		 * /rss/channel/[title,link,description,...] | /[copyright]|item/**
		 */
		var allChannels =feed.getElementsByTagName("channel");
		var channel;
		if(allChannels && allChannels.length > 0){
			channel = allChannels[0];
			if(channel){
				title = getFirstDomNodeContents(channel.getElementsByTagName("title"));
				copyright = getFirstDomNodeContents(channel.getElementsByTagName("copyright"));
				items = channel.getElementsByTagName("item");
				if(items && items.length){
					entries = Math.min(5, items.length);
					for(i=0; i < entries; i++){
						item = items[i];
						line = document.createElement("div");
						body = document.createElement("div");
						link = document.createElement("a");
						line.appendChild(link);
						link.href = getFirstDomNodeContents(item.getElementsByTagName("link"));
						link.target = "_blank";
						link.innerHTML = getFirstDomNodeContents(item.getElementsByTagName("title"));
						desc = getFirstDomNodeContents(item.getElementsByTagName("description"));
						if(desc){
							body.innerHTML = desc.substr(0, Math.min(120, desc.length));
						}
						line.appendChild(body);
						feedBody.appendChild(line);		
					}
				}				
			}
		}
	}
	else { //text
		title = "[RAW RESPONSE]"
		var pre = document.createElement("pre");
		if (feed && typeof(feed) === "string") {
			//Apps are incorrectly escaped, so you need to construct the escape sequence
			var es = "&";
			var gt = "gt;";
			var lt = "lt;";
			tmp = feed.replace("<", es + lt, "g");
			tmp = tmp.replace(">", es + gt, "g");
			pre.innerHTML = tmp;
			feedBody.appendChild(pre);
		}
		else {
			feedBody.innerHTML = "Huh?";
		}
	}
	
	titleElem.innerHTML = title;
	
	elem.appendChild(titleElem);
	//honor the copyright
	if(copyright != null){
		var cp = document.createElement("div");
		cp.style.fontSize="7pt;"
		cp.style.color="#FFA";
		cp.style.fontWeight="bold";
		cp.style.fontFamily="Arial";
		cp.innerHTML = "Copyright " + copyright;
		elem.appendChild(cp);
	}
	elem.appendChild(feedBody);
}

/**
 * Convenience method to get first items from a nodelist
 * @param {Object} nodelist
 */
function getFirstDomNodeContents(nodelist){
	if(nodelist && nodelist.length > 0){
		return nodelist[0].textContent;
	}
	else{
		return null;
	}
}


/**
 * Word of the day
 */
function loadWordOfTheDay(){
    var feedUrl = "http://wordsmith.org/awad/rss1.xml";
	  
      // Initialize the request to treat it as a feed
	  var params = {};
	  params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.FEED;
	  params[gadgets.io.RequestParameters.NUM_ENTRIES] = 1;
	  gadgets.io.makeRequest(feedUrl, loadWODCallback, params);
}

function loadWODCallback(response){
	var feed = response.data;
	var elem = document.getElementById("wordoftheday");
	var desc = document.getElementById("wodDesc");
	var copy = document.getElementById("wodcopyright");
	elem.innerHTML = "";
	copy.innerHTML = feed.copyright;
	if(response.errors && response.errors.length > 0){
		elem.innerHTML = "error"
	}
	else{
		var wod = feed.items[0];
		elem.innerHTML = "<a target='_blank' href=\"" + wod.link + "\">" + wod.title + "</a>";
		desc.innerHTML = wod.description;
	}
	
}


function searchImage(){
	var searchUrl = "http://search.yahooapis.com/ImageSearchService/V1/imageSearch?appid=YahooDemo&query=##term##&output=json"	;
	
	var term = document.getElementById("imageTerm").value;
	if(!term || term == "") term = "world";
	
	var searchUrl = searchUrl.replace("##term##", term);
	makeJsonpRequest(searchUrl, "loadSearchCallback");	
}

function loadSearchCallback(data){
	// data.ResultSet.Result[@Title, @Url]
	
	var elem = document.getElementById("rssFeed");
	elem.innerHTML = "";
	
	var line, img;
	var result;
	for(var i=0;i < data.ResultSet.Result.length; i++){
		result = data.ResultSet.Result[i];
		line = document.createElement("div");
		img = document.createElement("img");
		line.appendChild(img);
		img.src = result.Url;
		img.setAttribute("height", "60");
		elem.appendChild(line);
	}
}



/**
 * Make a JSONP request to any url serving JSON data by utilizing
 * the google ajax feed service
 * @param {Object} url
 * @param {Object} callback
 */
function makeJsonpRequest(url, callback){
	var ajaxurl =url + "&callback=" + callback;
	
	var headElem = document.getElementsByTagName("head")[0];
	
	var scriptElem = document.createElement("script");
	scriptElem.src = ajaxurl;
	headElem.appendChild(scriptElem);	
}





function loadInitializer(){
	try{
		loadWordOfTheDay();
	}
	catch(ex){}
	try{
		initializeGame();
	}
	catch(ex){}
}



//register to look for initial data
gadgets.util.registerOnLoadHandler(loadInitializer);


</script>

<style>

#infoblock{
float:right;
width:300px;
height:500px;

}

#gameboard{
border-collapse: collapse;
border:2px solid green;
border-radius:3px;
padding:5px;
empty-cells:show;
font-size: 30pt;
font-family: courier;
font-weight:bold;
color:#fff;
}

#gameboard td{
background:#555;
color:#fff;
border:1px solid white;
text-align:center;
width:120px;
height:120px;
}

#messages{
	height:20px;
	overflow:auto;
}

#rssFeed{
	border: 3px inset #a30;
	background: #55a;
	color:#000;
	height:250px;
	width:300px;
	overflow:auto;
}

</style>


<div id="infoblock">
<form onsubmit="return false;">

<textarea id="txtName" name="name" ></textarea>
<button onclick="setNameData()">Set Name</button>

<div id="myinfo">



</div>

<h3 style="color:#388;">Word of the day is: <span id="wordoftheday"></span></h3>
<div id="wodDesc"></div>
<div style="font-size:8pt;">Wordsmith.org word of the day</div>
<div id="wodcopyright" style="font-size:8pt;"></div>
<div id="feedItemControl">
	<h1>Feed Items</h1>
	<div id="feedItemErrors"></div>
	<div>
		<label for="selFeedSource" >Feed</label>
		<select name="feedSource" id="selFeedSource" >
			<option value="http://wordsmith.org/awad/rss1.xml">Word of the Day</option>
			<option value="http://feeds.digg.com/digg/popular.rss" selected="selected">Digg</option>
			<option value="http://news.cnet.com/8300-1009_3-83.xml">CNet</option>
			<option value="http://feeds2.feedburner.com/ICanHasCheezburger">LOL Cats</option>
			<option value="http://feeds2.feedburner.com/Mashable">Mashable</option>
			<option value="http://rss.slashdot.org/Slashdot/slashdot">Slashdot</option>
			<option value="http://rss.hulu.com/HuluRecentlyAddedVideos?format=xml">Hulu</option>
			
		</select>
		
		<div>
			<input type="checkbox" id="chkRefreshFeed" ><label for="chkRefreshFeed">Auto Refresh Feed</label>
		</div>

	</div>
		<input type="radio" name="feedformat" value="FEED" checked="checked">Feed</input>		
		<input type="radio" name="feedformat" value="DOM">XML</input>
		<input type="radio" name="feedformat" value="TEXT">Text</input>	
		<br />	
	
	<button onclick="loadFeed()">Load Feed</button><br />
	<input id="imageTerm" />
	<button onclick="searchImage()">Search Images</button><br />

<div id="rssFeed">
</div>






</div>




<div id="otherinfo" >
Other User ID: 
<input type="text" id="txtOtherUserID" />	
<button onclick="getOtherNameData()">Get Name Data</button>
</div>

<div style="font-family:courier,monotype;color:#555;height:150px;overflow:auto;" id="messages" >
</div>

</form>
</div>

<table id="gameboard" onclick="makePlayerMove(event)">

<tr>
<td gamecell="0"></td>
<td gamecell="1"></td>
<td gamecell="2"></td>
</tr>
<tr>
<td gamecell="3"></td>
<td gamecell="4"></td>
<td gamecell="5"></td>
</tr>
<tr>
<td gamecell="6"></td>
<td gamecell="7"></td>
<td gamecell="8"></td>
</tr>

</table>

<button onclick="initializeGame();" >New Game</button>

<button onclick="loadAppData(null);" >Load Data</button>

